package com.cardone.persistent.support;

import java.util.List;

import lombok.Setter;

import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang.ArrayUtils;

import com.cardone.common.dto.PaginationDto;
import com.cardone.common.po.PoBase;
import com.cardone.context.Attributes;
import com.cardone.context.ContextHolder;
import com.cardone.context.DictionaryException;
import com.cardone.persistent.builder.Model;
import com.cardone.persistent.builder.ModelArgs;
import com.cardone.persistent.builder.ModelUtils;

/**
 * 简易jdbc dao
 *
 * @author yaohaitao
 *
 */
@Setter
public abstract class SimpleJdbcDao<D extends PoBase> implements SimpleDao<D> {
	/**
	 * 代码名称集合
	 */
	protected String[] codeNames;

	protected String deleteByCode;

	protected String deleteByIds;

	protected String find;

	protected String findByCode;

	protected String findById;

	protected String findByLikeCode;

	/**
	 * 标识名称集合
	 */
	protected String[] idNames;

	protected String insertByCode;

	protected String insertByNotExistsCode;

	protected String readByCode;

	protected String readByLikeCode;

	protected String updateByCode;

	/**
	 * 更新名称集合
	 */
	protected String[] updateNames = { Attributes.parentId.name(), Attributes.typeId.name(), Attributes.code.name(), Attributes.name.name(), Attributes.value.name(), Attributes.remark.name() };

	@Override
	public int deleteByCode(final D deleteDto, final String... whereAndEqProperties) {
		return this.deleteByCode(this.deleteByCode, deleteDto, whereAndEqProperties);
	}

	protected int deleteByCode(final String deleteId, final D deleteDto, final String... whereAndEqProperties) {
		final Model model = ModelUtils.newModel(deleteDto.getAttrs());

		ModelUtils.put(model, new ModelArgs(deleteDto).setIsSimple(true));

		if (ArrayUtils.isNotEmpty(whereAndEqProperties)) {
			ModelUtils.put(model, new ModelArgs(deleteDto, whereAndEqProperties));
		}

		return ContextHolder.getBean(JdbcTemplateSupport.class).update(deleteId, model);
	}

	@Override
	public int deleteByIds(final D deleteDto) {
		return this.deleteByIds(this.deleteByIds, deleteDto);
	}

	protected int deleteByIds(final String deleteId, final D deleteDto) {
		final Model model = ModelUtils.newModel(deleteDto.getAttrs());

		model.put(Attributes.ids.name(), deleteDto.getIds());

		model.putTrue(Model.Keys.whereAndBetween.stringValue(), Attributes.sysdate.name());

		return ContextHolder.getBean(JdbcTemplateSupport.class).update(deleteId, model);
	}

	@Override
	public <P> P findByCode(final Class<P> mappedClass, final D findDto, final String... whereAndEqProperties) {
		return this.findByCode(mappedClass, this.findByCode, findDto, whereAndEqProperties);
	}

	protected <P> P findByCode(final Class<P> mappedClass, final String findId, final D findDto, final String... whereAndEqProperties) {
		final Model model = ModelUtils.newModel(findDto.getAttrs());

		ModelUtils.put(model, new ModelArgs(findDto).setIsSimple(true));

		if (ArrayUtils.isNotEmpty(whereAndEqProperties)) {
			ModelUtils.put(model, new ModelArgs(findDto, whereAndEqProperties));
		}

		return ContextHolder.getBean(JdbcTemplateSupport.class).find(mappedClass, findId, model);
	}

	@Override
	public <P> P findByCodeOrId(final Class<P> mappedClass, final D findDto) {
		return this.findByCodeOrId(mappedClass, this.findByCode, findDto);
	}

	protected <P> P findByCodeOrId(final Class<P> mappedClass, final String findId, final D findDto) {
		final Model model = ModelUtils.newModel(findDto.getAttrs());

		final String[] whereAndEqProperties = this.isBlankById(findDto) ? this.codeNames : this.idNames;

		if (ArrayUtils.isNotEmpty(whereAndEqProperties)) {
			ModelUtils.put(model, new ModelArgs(findDto, whereAndEqProperties));
		}

		model.putTrue(Model.Keys.whereAndBetween.stringValue(), Attributes.sysdate.name());

		return ContextHolder.getBean(JdbcTemplateSupport.class).find(mappedClass, findId, model);
	}

	@Override
	public <P> P findById(final Class<P> mappedClass, final D findDto) {
		return this.findById(mappedClass, this.findById, findDto);
	}

	protected <P> P findById(final Class<P> mappedClass, final String findId, final D findDto) {
		final Model model = ModelUtils.newModel(findDto.getAttrs());

		ModelUtils.put(model, new ModelArgs(findDto, this.idNames).setIsSimple(true));

		return ContextHolder.getBean(JdbcTemplateSupport.class).find(mappedClass, findId, model);
	}

	@Override
	public <P> List<P> findList(final Class<P> mappedClass) {
		return this.findListBy(mappedClass, this.find, null);
	}

	protected <P> List<P> findListBy(final Class<P> mappedClass, final String findId, final D findListDto, final String... whereAndEqProperties) {
		if (findListDto == null) {
			return ContextHolder.getBean(JdbcTemplateSupport.class).findList(mappedClass, findId, null);
		}

		final Model model = ModelUtils.newModel(findListDto.getAttrs());

		ModelUtils.put(model, new ModelArgs(findListDto).setIsSimple(true));

		if (ArrayUtils.isNotEmpty(whereAndEqProperties)) {
			ModelUtils.put(model, new ModelArgs(findListDto, whereAndEqProperties));
		}

		return ContextHolder.getBean(JdbcTemplateSupport.class).findList(mappedClass, findId, model);
	}

	@Override
	public <P> List<P> findListByCode(final Class<P> mappedClass, final D findListDto, final String... whereAndEqProperties) {
		return this.findListBy(mappedClass, this.find, findListDto, whereAndEqProperties);
	}

	@Override
	public <P> List<P> findListByLikeCode(final Class<P> mappedClass, final D findListDto) {
		return this.findListBy(mappedClass, this.findByLikeCode, findListDto);
	}

	@Override
	public int insertByCode(final D insertDto) {
		return this.insertByCode(this.insertByCode, insertDto);
	}

	/**
	 * 插入
	 *
	 * @param insertDtoList
	 *          对象集合
	 *
	 * @return 影响行数
	 */
	@Override
	public int insertByCode(final List<D> insertDtoList) {
		if (CollectionUtils.isEmpty(insertDtoList)) {
			return 0;
		}

		int insertCount = 0;

		for (final D insertDto : insertDtoList) {
			insertCount += this.insertByCode(insertDto);
		}

		return insertCount;

	}

	protected int insertByCode(final String insertId, final D insertDto) {
		final Model model = ModelUtils.newModel(insertDto.getAttrs());

		if (this.isBlankById(insertDto)) {
			this.setId(insertDto);
		}

		ModelUtils.put(model, new ModelArgs(Model.Keys.insert.stringValue(), insertDto));

		model.putTrue(Model.Keys.whereAndBetween.stringValue(), Attributes.sysdate.name());

		return ContextHolder.getBean(JdbcTemplateSupport.class).update(insertId, model);
	}

	protected int insertByCode(final String insertId, final List<D> insertDtoList) {
		if (CollectionUtils.isEmpty(insertDtoList)) {
			return 0;
		}

		int insertCount = 0;

		for (final D insertDto : insertDtoList) {
			insertCount += this.insertByCode(insertId, insertDto);
		}

		return insertCount;
	}

	@Override
	public int insertByNotExistsCode(final D insertDto) {
		return this.insertByNotExistsCode(this.insertByNotExistsCode, insertDto);
	}

	@Override
	public int insertByNotExistsCode(final List<D> insertDtoList) {
		if (CollectionUtils.isEmpty(insertDtoList)) {
			return 0;
		}

		int insertCount = 0;

		for (final D insertDto : insertDtoList) {
			insertCount += this.insertByNotExistsCode(insertDto);
		}

		return insertCount;
	}

	protected int insertByNotExistsCode(final String insertId, final D insertDto) {
		final Model model = ModelUtils.newModel(insertDto.getAttrs());

		String[] whereAndEqProperties;

		if (this.isBlankById(insertDto)) {
			this.setId(insertDto);

			whereAndEqProperties = this.codeNames;
		} else {
			whereAndEqProperties = this.idNames;
		}

		if (ArrayUtils.isNotEmpty(whereAndEqProperties)) {
			ModelUtils.put(model, new ModelArgs(insertDto, whereAndEqProperties));
		}

		ModelUtils.put(model, new ModelArgs(Model.Keys.insert.stringValue(), insertDto));

		model.putTrue(Model.Keys.whereAndBetween.stringValue(), Attributes.sysdate.name());

		return ContextHolder.getBean(JdbcTemplateSupport.class).update(insertId, model);
	}

	public int insertByNotExistsCode(final String insertId, final List<D> insertDtoList) {
		if (CollectionUtils.isEmpty(insertDtoList)) {
			return 0;
		}

		int insertCount = 0;

		for (final D insertDto : insertDtoList) {
			insertCount += this.insertByNotExistsCode(insertId, insertDto);
		}

		return insertCount;
	}

	protected abstract boolean isBlankById(final D dto);

	protected <P> PaginationDto<P> paginationBy(final Class<P> mappedClass, final String readId, final String findId, final D paginationDto) {
		final Model model = ModelUtils.newModel(paginationDto.getAttrs());

		ModelUtils.put(model, new ModelArgs(paginationDto).setIsSimple(true));

		return ContextHolder.getBean(JdbcTemplateSupport.class).pagination(mappedClass, new PaginationArgs(readId, findId, paginationDto), model);
	}

	@Override
	public <P> PaginationDto<P> paginationByLikeCode(final Class<P> mappedClass, final D paginationDto) {
		return this.paginationBy(mappedClass, this.readByLikeCode, this.findByLikeCode, paginationDto);
	}

	@Override
	public Integer readByCodeNqIdForCount(final D readDto) {
		return this.readByCodeNqIdForCount(this.readByCode, readDto);
	}

	protected Integer readByCodeNqIdForCount(final String readId, final D readDto) {
		final Model model = ModelUtils.newModel(readDto.getAttrs());

		ModelUtils.put(model, new ModelArgs(readDto, this.codeNames));

		if (!this.isBlankById(readDto)) {
			ModelUtils.put(model, new ModelArgs(Model.Keys.whereAndNq.stringValue(), readDto, this.idNames));
		}

		model.putTrue(Model.Keys.whereAndBetween.stringValue(), Attributes.sysdate.name());

		return ContextHolder.getBean(JdbcTemplateSupport.class).read(Integer.class, readId, model);
	}

	@Override
	public <P> P saveByIdOrCode(final Class<P> mappedClass, final D saveDto) {
		return this.saveByIdOrCode(mappedClass, this.insertByNotExistsCode, this.updateByCode, this.findByCode, saveDto);
	}

	protected <P> P saveByIdOrCode(final Class<P> mappedClass, final String insertId, final String updateId, final String findId, final D saveDto) {
		if (ArrayUtils.isEmpty(this.codeNames) && this.isBlankById(saveDto)) {
			this.insertByCode(saveDto);

			return this.findByCodeOrId(mappedClass, findId, saveDto);
		}

		final int updateCount = this.update(updateId, saveDto);

		if (updateCount > 1) {
			throw new DictionaryException("更新到多个数据").setTypeCode("update").setCode("multiple");
		}

		if (updateCount < 1) {
			this.insertByNotExistsCode(insertId, saveDto);
		}

		return this.findByCodeOrId(mappedClass, findId, saveDto);
	}

	protected abstract void setId(final D dto);

	protected int update(final String updateId, final D updateDto) {
		final String[] whereAndEqProperties = this.isBlankById(updateDto) ? this.codeNames : this.idNames;

		return this.update(updateId, updateDto, whereAndEqProperties, this.updateNames);
	}

	protected int update(final String updateId, final D updateDto, final String... updateNames) {
		final String[] whereAndEqProperties = this.isBlankById(updateDto) ? this.codeNames : this.idNames;

		return this.update(updateId, updateDto, whereAndEqProperties, updateNames);
	}

	protected int update(final String updateId, final D updateDto, final String[] whereAndEqProperties, final String... updateNames) {
		final Model model = ModelUtils.newModel(updateDto.getAttrs());

		ModelUtils.put(model, new ModelArgs(Model.Keys.update.stringValue(), updateDto, updateNames));

		if (ArrayUtils.isNotEmpty(whereAndEqProperties)) {
			ModelUtils.put(model, new ModelArgs(updateDto, whereAndEqProperties));
		}

		return ContextHolder.getBean(JdbcTemplateSupport.class).update(updateId, model);
	}

	@Override
	public int updateByCode(final D updateDto) {
		return this.update(this.updateByCode, updateDto);
	}

	@Override
	public int updateList(final List<D> updateDtoList) {
		return this.updateList(this.updateByCode, updateDtoList);
	}

	protected int updateList(final String updateId, final List<D> updateDtoList) {
		if (CollectionUtils.isEmpty(updateDtoList)) {
			return 0;
		}

		int updateCount = 0;

		for (final D updateDto : updateDtoList) {
			updateCount += this.update(updateId, updateDto);
		}

		return updateCount;
	}

	protected int updateList(final String updateId, final List<D> updateDtoList, final String... updateNames) {
		if (CollectionUtils.isEmpty(updateDtoList)) {
			return 0;
		}

		int updateCount = 0;

		for (final D updateDto : updateDtoList) {
			updateCount += this.update(updateId, updateDto, updateNames);
		}

		return updateCount;
	}

	protected int updateList(final String updateId, final List<D> updateDtoList, final String[] whereAndEqProperties, final String... updateNames) {
		if (CollectionUtils.isEmpty(updateDtoList)) {
			return 0;
		}

		int updateCount = 0;

		for (final D updateDto : updateDtoList) {
			updateCount += this.update(updateId, updateDto, whereAndEqProperties, updateNames);
		}

		return updateCount;
	}
}